package lapsnake.model.data;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;

import owg.util.Calc;
import lapsnake.model.LapNode;

abstract class AbstractChildField<T extends LapNode> implements ChildField<T>
{	
	/**Veto listeners*/
	private ArrayList<WeakReference<VetoableChangeListener>> vetoListeners = new ArrayList<>();
	
	protected LapNode owner;
	/**The type of the property*/
	protected Class<T> type;
    /**Whether there are any listeners that have not been notified of a new change.*/
	protected boolean dirty = false;
	/**Whether a NewLine annotation has been found for this field*/
	protected boolean newLine = false;
    /**Typed listeners*/
	protected ArrayList<WeakReference<ChildListener<? super T>>> childListeners = new ArrayList<>();
	
	protected boolean indented;
    
	public AbstractChildField(LapNode owner, Class<T> childType, boolean indented)
	{
		this.owner = owner;
		this.type = childType;
		this.indented = indented;
	}
	
	@Override
	public boolean isIndented()
	{
		return indented;
	}
	
	@Override
	public LapNode getOwner()
	{
		return owner;
	}
	@Override
	public Class<T> getType()
	{
		return type;
	}
	
	@Override
	public void addListener(ChildListener<? super T> l)
	{
		if(l == null)
		{
		    System.err.println("Cannot add null as listener to "+this);
		    return;
		}
		int oldIndex = Calc.indexOfWeakReference(childListeners, l);
		if(oldIndex != -1)
		{
		    System.err.println("Warning: Tried to add "+l+" as listener to "+this+" but it was already added at "+oldIndex);
		    return;
		}
		childListeners.add(new WeakReference<ChildListener<? super T>>(l));
	}
	
	@Override
	public boolean removeListener(ChildListener<? super T> l)
	{
		int index = Calc.indexOfWeakReference(childListeners, l);
		if(index == -1)
			return false;
		childListeners.remove(index);
		return true;
	}
	
	protected void checkVetos(Object exclude, T newValue) throws PropertyVetoException
	{
		Iterator<WeakReference<VetoableChangeListener>> iterator = vetoListeners.iterator();
		while (iterator.hasNext())
		{
			WeakReference<VetoableChangeListener> v = iterator.next();
			VetoableChangeListener l = v.get();
			if(l == null)
				iterator.remove();
			else
				l.vetoableChange(new PropertyChangeEvent(exclude, toString(), get(), newValue));
		}
	}
	
	protected void fireEvents(Object exclude)
	{
		dirty = true;
		
		Iterator<WeakReference<ChildListener<? super T>>> it = childListeners.iterator();
		while(it.hasNext())
		{
			ChildListener<? super T> next = it.next().get();
			if(next != null)
			{
				if(next != exclude)
				{
					next.valueChanged(this);
				}
				if(!dirty)
					return;
			}
			else
				it.remove();//Clean up destroyed reference
		}
		dirty = false;
	}
	@Override
	public void addVetoListener(VetoableChangeListener l)
	{
		if(l == null)
		{
			System.err.println("Cannot add null as action listener to "+this);
			return;
		}
		int oldIndex = Calc.indexOfWeakReference(vetoListeners, l);
		if(oldIndex != -1)
		{
			System.err.println("Warning: Tried to add "+l+" as veto listener to "+this+" but it was already added at "+oldIndex);
			return;
		}
		vetoListeners.add(new WeakReference<VetoableChangeListener>(l));
	}
	@Override
	public boolean removeVetoListener(VetoableChangeListener l)
	{
		int index = Calc.indexOfWeakReference(vetoListeners, l);
		if(index == -1)
			return false;
		vetoListeners.remove(index);
		return true;
	}
	
	@Override
	public void setNLAnnotation()
	{
		newLine = true;
	}
	public boolean hasNLAnnotation() 
	{
		return newLine;
	}
}
